﻿using System.Dynamic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

namespace SDK_Sample_NET6
{
    /// <summary>
    /// DynamicObject类型扩展
    /// </summary>
    public static class DynamicExtensions
    {
        public static Delegate Getter(this IDictionary<string, object> _, string name)
        {
            return (IDictionary<string, object> o) => { return Value(o, name); };
        }

        public static Delegate Getter(this IDynamicMetaObjectProvider provider, string name)
        {
            Type scope = provider.GetType();
            ParameterExpression parameter = Expression.Parameter(typeof(object));
            DynamicMetaObject metaObject = provider.GetMetaObject(parameter);
            GetMemberBinder binder = (GetMemberBinder)Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, scope, new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) });
            DynamicMetaObject operation = metaObject.BindGetMember(binder);
            BlockExpression block = Expression.Block(
                Expression.Label(CallSiteBinder.UpdateLabel),
                operation.Expression
            );
            LambdaExpression lambda = Expression.Lambda(block, parameter);
            return lambda.Compile();
        }

        public static Delegate Getter(this object obj, string name)
        {
            Type type = obj.GetType();

            return typeof(IDictionary<string, object>).IsAssignableFrom(type)
                ? ((IDictionary<string, object> o) => { return Value(o, name); })
                : typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type)
                ? (Delegate)((Func<IDynamicMetaObjectProvider, string, Delegate>)Getter).DynamicInvoke(obj, name)
                : throw new NotImplementedException();
        }

        public static Delegate Setter(this IDictionary<string, object> _, string name)
        {
            return (IDictionary<string, object> o, object value) => { Value(o, name, value); };
        }

        public static Delegate Setter(this IDynamicMetaObjectProvider provider, string name)
        {
            Type scope = provider.GetType();
            ParameterExpression[] parameters = new ParameterExpression[]
                {
                    Expression.Parameter(typeof(object)),
                    Expression.Parameter(typeof(object))
                };
            DynamicMetaObject metaObject = provider.GetMetaObject(parameters[0]);
            SetMemberBinder binder = (SetMemberBinder)Microsoft.CSharp.RuntimeBinder.Binder.SetMember(0, name, scope, new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null), Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) });
            DynamicMetaObject operation = metaObject.BindSetMember(binder, new DynamicMetaObject(parameters[1], BindingRestrictions.Empty));
            BlockExpression block = Expression.Block(
                Expression.Label(CallSiteBinder.UpdateLabel),
                operation.Expression
            );
            LambdaExpression lambda = Expression.Lambda(block, parameters[0], parameters[1]);
            return lambda.Compile();
        }

        public static Delegate Setter(this object obj, string name)
        {
            Type type = obj.GetType();

            return typeof(IDictionary<string, object>).IsAssignableFrom(type)
                ? ((IDictionary<string, object> o, object value) => { Value(o, name, value); })
                : typeof(IDynamicMetaObjectProvider).IsAssignableFrom(type)
                ? (Delegate)((Func<IDynamicMetaObjectProvider, string, Delegate>)Setter).DynamicInvoke(obj, name)
                : throw new NotImplementedException();
        }
        /// <summary>
        /// 获取属性或字典键值
        /// </summary>
        public static dynamic Value(this IDynamicMetaObjectProvider provider, string name)
        {
            Delegate getter = provider.Getter(name);
            return getter.DynamicInvoke(provider);
        }
        /// <summary>
        /// 获取属性或字典键值
        /// </summary>
        public static dynamic Value(this IDictionary<string, object> dictionary, string name)
        {
            return dictionary.TryGetValue(name, out object value) ? value : (dynamic)null;
        }
        /// <summary>
        /// 设置属性或字典键值
        /// </summary>
        public static void Value(this IDynamicMetaObjectProvider provider, string name, object value)
        {
            Delegate setter = provider.Setter(name);
            _ = setter.DynamicInvoke(provider, value);
        }
        /// <summary>
        /// 设置属性或字典键值
        /// </summary>
        public static void Value(this IDictionary<string, object> dictionary, string name, object value)
        {
            dictionary[name] = value;
        }
    }
}
